package com.wxc.thread;

import com.wxc.common.User;
import com.wxc.util.ConnectionUtil;
import com.wxc.util.SocketUtil;
import com.wxc.vo.Message;
import com.wxc.vo.Resp;
import com.wxc.vo.RespCode;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.Socket;
import java.util.Set;

/**
 * @author wxc
 * @date 2021年10月01日 18:56
 */

@EqualsAndHashCode(callSuper = true)
@Data
@Slf4j
public class ConnectionThread extends Thread {

    private Socket socket;
    private User user;
    // 线程池每个线程一份
    private ConnectionUtil connectionUtil = null;

    public ConnectionThread(Socket socket, User user) {
        this.socket = socket;
        this.user = user;
    }

    @Override
    public void run() {
        ManageConnectionThreads.add(user.getUserName(), this);
        connectionUtil = ThreadHolder.threadLocal.get();
        log.info(connectionUtil.toString());
        log.info(Thread.currentThread().getName() + " connect " + user.getUserName());

        while (true) {
            Message<Object> receiveMessage = connectionUtil.receive(socket);
            if(receiveMessage == null) {
                log.error("client error,current thread quit");
                ManageConnectionThreads.remove(user.getUserName());
                break;
            };
            Message.MessageType type = receiveMessage.getType();
            if (type == Message.MessageType.SHOW_USERS) {
                showUserNameList();
            } else if (type == Message.MessageType.QUIT) {
                quit();
                break;
            } else if (type == Message.MessageType.CHAT) {
                chat(receiveMessage.getSender(), receiveMessage.getReceiver(), receiveMessage.getData());
            } else if (type == Message.MessageType.CHAT_ALL) {
                chatAll(receiveMessage.getSender(), receiveMessage.getData());
            } else if (type == Message.MessageType.FILE) {
                sendFile(receiveMessage.getSender(), receiveMessage.getReceiver(), receiveMessage.getData());
            }
        }
    }

    private void sendFile(String sender, String receiver, Object data) {
        ConnectionThread connectionThread = ManageConnectionThreads.get(receiver);
        if (connectionThread != null) {
            log.info("Client:{} send file to Client:{},data:{}", sender, receiver, data);
            Message<Resp<Object>> sendMessage = new Message<>();
            sendMessage.setType(Message.MessageType.FILE);
            sendMessage.setData(new Resp<>(data, RespCode.SUCCESS));
            sendMessage.setSender(sender);
            sendMessage.setReceiver(receiver);
            connectionUtil.send(connectionThread.getSocket(), sendMessage);
        }
        // not online
        else {
            log.info("Client:{} send file to Client:{},data:{} fail", sender, receiver, data);
            Message<Resp<Object>> sendMessage = new Message<>();
            sendMessage.setType(Message.MessageType.FILE);
            sendMessage.setData(new Resp<>(data, RespCode.CHAT_ERROR));
            connectionUtil.send(socket, sendMessage);
        }
    }

    private void chatAll(String sender, Object data) {
        Set<String> usernames = ManageConnectionThreads.getUsernames();
        for (String username : usernames) {
            if (username.equals(sender)) continue;
            // send all
            ConnectionThread connectionThread = ManageConnectionThreads.get(username);
            log.info("Client:{} chat all,data:{}", sender, data);
            Message<Resp<Object>> sendMessage = new Message<>();
            sendMessage.setType(Message.MessageType.CHAT_ALL);
            sendMessage.setData(new Resp<>(data, RespCode.SUCCESS));
            sendMessage.setSender(sender);
            connectionUtil.send(connectionThread.getSocket(), sendMessage);
        }
    }

    private void chat(String sender, String receiver, Object data) {
        ConnectionThread connectionThread = ManageConnectionThreads.get(receiver);
        if (connectionThread != null) {
            log.info("Client:{} chat to Client:{},data:{}", sender, receiver, data);
            Message<Resp<Object>> sendMessage = new Message<>();
            sendMessage.setType(Message.MessageType.CHAT);
            sendMessage.setData(new Resp<>(data, RespCode.SUCCESS));
            sendMessage.setSender(sender);
            sendMessage.setReceiver(receiver);
            connectionUtil.send(connectionThread.getSocket(), sendMessage);
        }
        // not online
        else {
            log.info("Client:{} chat to Client:{},data:{} fail", sender, receiver, data);
            Message<Resp<Object>> sendMessage = new Message<>();
            sendMessage.setType(Message.MessageType.CHAT);
            sendMessage.setData(new Resp<>(data, RespCode.CHAT_ERROR));
            connectionUtil.send(socket, sendMessage);
        }
    }

    private void showUserNameList() {
        Set<String> usernames = ManageConnectionThreads.getUsernames();
        log.info("Client:{} invoke showUserNameList function,result:{}", user.getUserName(), usernames);
        Message<Resp<String>> sendMessage = new Message<>();
        sendMessage.setType(Message.MessageType.SHOW_USERS);
        sendMessage.setData(new Resp<>(usernames.toString(), RespCode.SUCCESS));
        connectionUtil.send(socket, sendMessage);
    }

    private void quit() {
        Message<Resp<String>> sendMessage = new Message<>();
        sendMessage.setType(Message.MessageType.QUIT);
        connectionUtil.send(socket, sendMessage);
        ManageConnectionThreads.remove(user.getUserName());
        user.setLoginStatus(false);
        log.info("Client:{} quit", user.getUserName());
        SocketUtil.close(socket);
    }

    /**
     * @date 2021-10-07 19:27
     * Server push Message
     */
    public void push(String s) {
        if(!user.getLoginStatus()) return;
        Message<Resp<String>> message = new Message<>();
        message.setType(Message.MessageType.PUSH);
        message.setSender("CLIENT");
        message.setData(new Resp<>(s, RespCode.SUCCESS));
        log.info("Server push Message: {}",s);
        connectionUtil.send(socket, message);
    }
}
